package cn.whitecao.agent.core.plugin;

import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.whitecao.agent.core.global.GlobalVal;
import cn.whitecao.agent.core.loaders.ExternalJarClassLoader;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 加载插件,自定义SPI没用JDK自带的
 * @author : BashfulHuang
 **/
@Slf4j
public class PluginLoader {
    private static final String SPI_CONFIG_FILE="META-INF/plugin.prop";
    /**
     * 加载插件
     * @param path 插件路径
     */
    public List<PluginDefinition> load(String path){
        ExternalJarClassLoader classLoader = new ExternalJarClassLoader(path);
        GlobalVal.externalJarClassLoader=classLoader;
        try {
            Enumeration<URL> resources = classLoader.getResources(SPI_CONFIG_FILE);
            List<URL> spiConfigFileUrls = IterUtil.toList(IterUtil.asIterator(resources));
            //加载
            return doLoad(spiConfigFileUrls,classLoader);
        } catch (IOException e) {
            throw new RuntimeException("获取插件资源异常");
        }
    }

    /**
     *  执行加载
     * @param spiConfigFileUrls url列表
     * @param classLoader 类加载器
     * @return 初始化好的类
     */
    private List<PluginDefinition> doLoad(List<URL> spiConfigFileUrls,ClassLoader classLoader){
        //读取配置文件并且过滤掉#注释的
        List<String> classNames = new ArrayList<>();
        List<PluginDefinition> pluginDefinitions=new ArrayList<>();
        for (URL url:spiConfigFileUrls) {
            List<String> lines = FileUtil.readLines(url, CharsetUtil.UTF_8);
            classNames.addAll(lines);
        }
        classNames=classNames.stream().filter(name->!name.startsWith("#")).collect(Collectors.toList());


        classNames.forEach(className->{
            try {
                Object instance = Class.forName(className,true,classLoader).newInstance();
                //如果没实现PluginDefinition接口会报错 插件加载失败做下记录即可,然后加载下一个
                PluginDefinition definition = (PluginDefinition) instance;
                pluginDefinitions.add(definition);
            } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
                log.info(className+" 加载失败");
            }
        });
        return pluginDefinitions;
    }


}
